#include <iostream>
#include <math.h>
using namespace std;

#define PICTTRANS_FORWARD 1
#define SIZE 4
#define PI 3.1416926

int Output(int *Array, int Size)
{
	cout << "\n";
	for (int i = 0; i < Size; i++)
	{
		for (int j = 0; j < Size; j++)
		{
			cout << Array[Size * i + j] << "\t";
		}
		cout << "\n";
	}
	return 1;
}

int setFourierMatrix(float *RMatrix, float *IMatrix, int Size)
{
	for(int i = 0; i< Size;i++)
	{
		for (int j = 0; j < Size; j++)
		{
			float arg = -2* PI * (float) i / Size * (float) j;
			RMatrix[Size*i+j] = cos(arg);
			IMatrix[Size*i+j] = sin(arg);
		}
	}
	return 1;
}

int HorisontalTransfer(int *nArray,int *nArray2, int *nArray3, int iHeight, int iWidth, float *RMatrix, float* IMatrix)
{
	int iWidth2 = iWidth / SIZE;
	int iHeight2 = iHeight / SIZE;
	int iSlidingW = 0;
	for (int h = 0; h < iHeight2; h++)
	{
		iSlidingW = (h * SIZE * iWidth);
		for (int w = 0; w < iWidth2; w++)
		{
// Block Cycle
			for (int i = 0; i < SIZE; i++)
			{
				for (int j = 0; j < SIZE; j++)
				{
					float R = 0;
					float I = 0;
					for (int n = 0; n < SIZE; n++)
					{
						R = R + RMatrix[SIZE*i+n] * (float) nArray[iSlidingW + n * iWidth + j];
						I = I + IMatrix[SIZE*i+n] * (float) nArray[iSlidingW + n * iWidth + j]; 
					}
					nArray2[iSlidingW + i * iWidth + j] = (int) R;
					nArray3[iSlidingW + i * iWidth + j] = (int) I;
				}
			}
// End Block Cycle
			iSlidingW = iSlidingW + SIZE;
		}
	}
	return 1;
}

int HorisontalTransferI(int *nArray1,int *nArray2, int *nArray3, int iHeight, int iWidth, float* RMatrix, float* IMatrix)
{
	int iWidth2 = iWidth / SIZE;
	int iHeight2 = iHeight / SIZE;
	int iSlidingW = 0;
	for (int h = 0; h < iHeight2; h = h++)
	{
		iSlidingW = (h * SIZE * iWidth);
		for (int w = 0; w < iWidth2; w = w++)
		{
// Block Cycle
			for (int i = 0; i < SIZE; i++)
			{
				for (int j = 0; j < SIZE; j++)
				{
					float R = 0;
					float I = 0;
					for (int n = 0; n < SIZE; n++)
					{
						R = R + (float)nArray1[iSlidingW + i * iWidth + n] * RMatrix[SIZE*j+n] + 
							(float)nArray2[iSlidingW + i * iWidth + n] * IMatrix[SIZE*j+n];
					}
					nArray3[iSlidingW + i * iWidth + j] = (int) (R / SIZE / SIZE);
				}
			}
// End Block Cycle
			iSlidingW = iSlidingW + SIZE;
		}
	}
	return 1;
}


int VerticalTransfer(int *nArray1, int *nArray2, int *nArray3, int *nArray4, int iHeight, int iWidth, float* RMatrix, float* IMatrix)
{
	int iWidth2 = iWidth / SIZE;
	int iHeight2 = iHeight / SIZE;
	int iSlidingW = 0;
	for (int h = 0; h < iHeight2; h = h++)
	{
		iSlidingW = (h * SIZE * iWidth);
		for (int w = 0; w < iWidth2; w = w++)
		{
// Block Cycle
			for (int i = 0; i < SIZE; i++)
			{
				for (int j = 0; j < SIZE; j++)
				{
					float R = 0;
					float I = 0;
					for (int n = 0; n < SIZE; n++)
					{
						R = R +	(float)nArray1[iSlidingW + i * iWidth + n] *		RMatrix[SIZE*n+j] -
								(float)nArray2[iSlidingW + i * iWidth + n] *		IMatrix[SIZE*n+j];
						I = I + (float)nArray2[iSlidingW + i * iWidth + n] *		RMatrix[SIZE*n+j] +
								(float)nArray1[iSlidingW + i * iWidth + n] *		IMatrix[SIZE*n+j];
					}
					nArray3[iSlidingW + i * iWidth + j] = (int) R;
					nArray4[iSlidingW + i * iWidth + j] = (int) I;
				}
			}
// End Block Cycle
			iSlidingW = iSlidingW + SIZE;
		}
	}
	return 1;
}

int VerticalTransferI(int *nArray1, int *nArray2, int *nArray3, int* nArray4, int iHeight, int iWidth, float* RMatrix, float* IMatrix)
{
	int iWidth2 = iWidth / SIZE;
	int iHeight2 = iHeight / SIZE;
	int iSlidingW = 0;
	for (int h = 0; h < iHeight2; h = h++)
	{
		iSlidingW = (h * SIZE * iWidth);
		for (int w = 0; w < iWidth2; w = w++)
		{
// Block Cycle
			for (int i = 0; i < SIZE; i++)
			{
				for (int j = 0; j < SIZE; j++)
				{
					float R = 0;
					float I = 0;
					for (int n = 0; n < SIZE; n++)
					{
						R = R + (float)nArray1[iSlidingW + n * iWidth + j] * RMatrix[SIZE*n+i] + 
							(float)nArray2[iSlidingW + n * iWidth + j] * IMatrix[SIZE*n+i];
						I = I + (float)nArray2[iSlidingW + n * iWidth + j] * RMatrix[SIZE*n+i] -
							(float)nArray1[iSlidingW + n * iWidth + j] * IMatrix[SIZE*n+i];
					}
					nArray3[iSlidingW + i * iWidth + j] = (int) R;
					nArray4[iSlidingW + i * iWidth + j] = (int) I;
				}
			}
// End Block Cycle
			iSlidingW = iSlidingW + SIZE;
		}
	}
	return 1;
}

int ComplexTransform(int *Image, int *RTransform, int *CTransform, int Height, int Width, float *RMatrix, float *IMatrix, int Flags)
{
	int *nArray1  = new int[Width * Height];
	int *nArray2 = new int[Width * Height];

	if (Flags & PICTTRANS_FORWARD)
	{
		HorisontalTransfer(Image, nArray1, nArray2, Height, Width, RMatrix, IMatrix);
		VerticalTransfer(nArray1, nArray2,RTransform, CTransform, Height, Width, RMatrix, IMatrix);
	}
	else
	{
		VerticalTransferI(RTransform, CTransform,nArray1,nArray2,Height, Width,RMatrix, IMatrix);
		HorisontalTransferI(nArray1,nArray2, Image, Height, Width, RMatrix, IMatrix);
	}

	delete[] nArray1;
	delete[] nArray2;
	return 1;
}
int main()
{
	float *RMatrix = new float[SIZE * SIZE];
	float *IMatrix = new float[SIZE * SIZE];
	setFourierMatrix(RMatrix, IMatrix, SIZE);
	int *Array = new int[SIZE * SIZE];
	srand(1);
	for (int i = 0; i<SIZE * SIZE;i++)
	{
		Array[i] = 125 + rand() * 50 / RAND_MAX;
	}
	int *Array1 = new int[SIZE * SIZE];
	int *Array2 = new int[SIZE * SIZE];
	Output(Array,SIZE);
	ComplexTransform(Array, Array1, Array2, SIZE, SIZE, RMatrix, IMatrix, PICTTRANS_FORWARD);
	ComplexTransform(Array, Array1, Array2, SIZE, SIZE, RMatrix, IMatrix, 0);
	Output(Array,SIZE);
	return 0;
}